﻿using ChholCmdPort;
using Sl.Support.McuBytes;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ChholConfig {
	public partial class MutNodeControl : PortUserForm {
		public MutNodeControl() : base() {
			InitializeComponent();
		}
		public static string ToolName = "多节点配置器";
		public static Bitmap ToolIcon = global::ChholConfig.Properties.Resources.multicast_30px;
		public static string ToolTip = "用于配置多个节点的参数。";

		bool clear_key = false;
		byte[] key = new byte[16];
		System.Security.Cryptography.SHA256 sha256;


		public Image passwd_image;
		public Image passwd_act;

		public MutNodeControl(RecvFanOut fan_out, Form main_form)
		: base(ToolIcon, 23, fan_out, ToolName) {
			InitializeComponent();
			PasswdGroup.Visible = commonTS.Enabled = false;
			sha256 = System.Security.Cryptography.SHA256.Create();
			this.Text = ToolName;
			this.Icon = Icon.FromHandle(ToolIcon.GetHicon());
			Port_user.dRecvPkg = pkg => {
				if (ERecvPkg != null) {
					foreach (Func<Pkg, bool> handler in ERecvPkg.GetInvocationList()) {
						if (handler(pkg)) {
							return true;
						}
					}
				}
				return false;
			};
			PasswdTB.Text = GenerateRandomPassword(24);
			passwd_image = passwdBTN.Image;
			passwd_act = Sl.Support.Forms.IconRecolor.reColorPng(passwd_image, Color.DodgerBlue);
		}
		event Func<Pkg, bool> ERecvPkg;

		protected override void turnOn() {
			commonTS.Enabled = true;
		}



		#region 密码设置相关的代码
		private string GenerateRandomPassword(int length) {
			const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*:;'{}[]()<>?/.,|~`+=_-";
			var random = new Random();
			var password = new string(Enumerable.Repeat(chars, length)
				.Select(s => s[random.Next(s.Length)]).ToArray());
			return password;
		}

		private void randomPasswdBTN_Click(object sender, EventArgs e) {
			PasswdTB.Text = GenerateRandomPassword(24);

		}
		private void PasswdTB_TextChanged(object sender, EventArgs e) {
			//从randomPasswdBTN_Click获取哈希值，然后将其存储在keyTB中
			//SHA256算法将PasswdTB.Text处理成一个哈希值
			//然后将其存储在keyTB中
			if (PasswdTB.Text.Length < 3) {
				keyTB.Text = "清除密码";
				clear_key = true;
			} else {
				byte[] Passwd = Encoding.UTF8.GetBytes(PasswdTB.Text);
				byte[] hash = sha256.ComputeHash(Passwd);
				//将哈希值混叠为16个byte
				for (int i = 0; i < 16; i++) {
					key[i] = (byte)(hash[i] ^ hash[i + 16]);
				}
				keyTB.Text = key.toHexStr();
				clear_key = false;
			}
		}
		private void ClearPasswdBTN_Click(object sender, EventArgs e) {
			PasswdTB.Text = "";
		}
		#endregion

		int node_count = 0;
		private async void scanBTN_Click(object sender, EventArgs e) {
			NodesDGV.Rows.Clear();
			node_count = 0;
			bool get_local_node = await scanLocalNode();
			if (!get_local_node) {
				this.StatusLAB.Text = "未找到本地节点";
				return;
			}
			await scanOtherNode();
			this.StatusLAB.Text = $"共计找到{node_count}个节点";

		}

		private async Task scanOtherNode() {
			this.StatusLAB.Text = "广播两次，扫描其他节点";
			int count = 0;
			for (int i = 0; i < 2; i++) {
				Pkg p = new Pkg();
				p.To_port = 102;
				p.Remote_addr = 0xffff;
				p.loadData((byte)'H');
				ERecvPkg += recvBcHandshake;
				Port_user.sendPkg(p);
			}
			bool recvBcHandshake(Pkg recv) {
				if ((recv[0] != (byte)'h') ||
					(recv.From_port != 102)) {
					return false;
				}
				count++;
				this.Invoke(new Action(() => findAddrEcho(recv.Remote_addr)));
				return true;
			}
			await Task.Delay(2000);
			ERecvPkg -= recvBcHandshake;
			this.StatusLAB.Text = $"收到{count}次回复。";
		}

		private async Task<bool> scanLocalNode() {
			this.StatusLAB.Text = "读取本地地址";
			Pkg p = new Pkg();
			p.To_port = 103;
			p.Remote_addr = 0xfff0;
			p.loadData((byte)'R');
			ERecvPkg += recvBcHandshake;
			bool is_echo_received = false;
			bool recvBcHandshake(Pkg rpkg) {
				if ((rpkg[0] != (byte)'r') ||
					(rpkg.From_port != 103)) {
					return false;
				}
				var addr = Ushort_Expand.ToUint16(rpkg[1], rpkg[2]);
				this.Invoke(new Action(() => findAddrEcho(addr, true)));
				is_echo_received = true;
				return true;
			}
			Port_user.sendPkg(p);
			for (int i = 0; i < 10; i++) {
				await Task.Delay(100);
				if (is_echo_received) {
					break;
				}
			}
			ERecvPkg -= recvBcHandshake;
			if (!is_echo_received) {
				this.StatusLAB.Text = "本地地址读取失败";
			}
			return is_echo_received;
		}

		private void findAddrEcho(ushort remote_addr, bool local = false) {
			foreach (DataGridViewRow row in NodesDGV.Rows) {
				if ((ushort)row.Cells[0].Tag == remote_addr) {
					return;
				}
			}
			int r = NodesDGV.Rows.Add(); node_count++;
			if (local) {
				NodesDGV.Rows[r].Cells[0].Value = $"本地{remote_addr:X4}";
			} else {
				NodesDGV.Rows[r].Cells[0].Value = $"远程{remote_addr:X4}";
			}
			NodesDGV.Rows[r].Cells[0].Tag = remote_addr;
		}
		private void sendLedOn(ushort addr) {
			Pkg spkg = new();
			spkg.To_port = 102;
			spkg.Remote_addr = addr;
			spkg.loadData((byte)'L');
			Port_user.sendPkg(spkg);

		}
		private void LedOnAllBTN_Click(object sender, EventArgs e) {
			sendLedOn(0xFFFF);
			sendLedOn(0xFFF0);
		}
		private void NodesDGVR_CellClick(object sender, DataGridViewCellEventArgs e) {
			int r_num = e.RowIndex;
			int col = e.ColumnIndex;
			if (col == 0) {
				if (r_num >= 0) {
					ushort addr = NodesDGV[col, r_num].Tag as ushort? ?? 0xfffe;
					if (addr != 0xffff) {
						sendLedOn(addr);
					}
				}
			} else if (col == 1) {
				if (dNodeSelected != null) {
					if (r_num >= 0) {
						bool current_value = NodesDGV[col, r_num].Value as bool? ?? false;
						rowSelectionChange(NodesDGV.Rows[r_num], !current_value);
					}
				}
			}

		}
		private bool is_selected_all = false;
		private void SelectAllBTN_Click(object sender, EventArgs e) {
			if (dNodeSelected != null) {
				is_selected_all = !is_selected_all;
				foreach (DataGridViewRow row in NodesDGV.Rows) {
					rowSelectionChange(row, is_selected_all);
				}
			}
		}

		Action<DataGridViewRow> dNodeSelected = null;
		Func<Task> dGoAction = null;
		private void rowSelectionChange(DataGridViewRow r, bool selected) {
			r.Cells[1].Value = selected;
			if (selected) {
				dNodeSelected(r);
			} else {
				r.Cells[2].Value = "未选中";
				r.Cells[2].Tag = null;
			}
		}
		async Task passwdSettingAction() {
			this.StatusLAB.Text = "找到所有远程节点的地址和本节点地址";
			(ushort addr, DataGridViewRow row) local_node = (0xfffe, null);
			List<(ushort addr, DataGridViewRow row)> remote_node = new(NodesDGV.Rows.Count);
			foreach (DataGridViewRow r in NodesDGV.Rows) {
				bool selected = r.Cells[1].Value as bool? ?? false;
				if (selected) {
					ushort addr = r.Cells[0].Tag as ushort? ?? 0xfffe;
					if (((string)r.Cells[0].Value).StartsWith("本地")) {
						local_node = (addr, r);
					} else {
						if (addr != 0xfffe) {
							remote_node.Add((addr, r));
						}
					}
				}
			}
			if (local_node.row == null) {
				this.StatusLAB.Text = "未找到本地节点";
				return;
			}
			this.StatusLAB.Text = "构造密码命令";
			string work_word;
			byte[] data = new byte[5 + 16];
			{
				work_word = "加密设置";
				if (clear_key == false) {
					data = new byte[5 + 16];
					int i = 0;
					//加密包形同：0001.110:/E f8 33 9a 12 16 B9 05 88 B6 01 D6 6F 5A FB 1C DB 76 5B 59 96
					data[i++] = (byte)'E';
					data[i++] = 0xf8;
					data[i++] = 0x33;
					data[i++] = 0x9a;
					data[i++] = 0x12;
					for (int j = 0; j < 16; j++) {
						data[i++] = key[j];
					}
				} else {
					work_word = "加密取消";

					data = new byte[5];
					int i = 0;
					data[i++] = (byte)'D';
					data[i++] = 0xf8;
					data[i++] = 0x33;
					data[i++] = 0x9a;
					data[i++] = 0x12;
				}
			}
			bool recvPkg(Pkg recv) {
				if ((recv[0] != (byte)'E') && (recv[0] != (byte)'D')) return false;
				if (recv.From_port != 110) return false;
				this.Invoke(() => {
					foreach (var node in remote_node) {
						if (recv.Remote_addr == node.addr) {
							node.row.Cells[2].Value = $"{work_word}成功。";
							return;
						}
					}
					if ((recv.Remote_addr == local_node.addr) ||
						(recv.Remote_addr == 0xfff0)) {
						local_node.row.Cells[2].Value = $"本机{work_word}成功。";
					}
				});
				return true;
			}
			ERecvPkg += recvPkg;
			if(remote_node.Count > 0) { 
				this.StatusLAB.Text = $"对所有远程模块发送{work_word}命令";
				foreach (var node in remote_node) {
					Pkg spkg = new();
					spkg.To_port = 110;
					spkg.Remote_addr = node.addr;
					spkg.loadData(data);
					Port_user.sendPkg(spkg);
					node.row.Cells[2].Value = $"{work_word}命令已经发出。";
				}
				await Task.Delay(1000);
			}
			this.StatusLAB.Text = $"本地模块{work_word}命令";
			{
				Pkg spkg = new();
				spkg.To_port = 110;
				spkg.Remote_addr = local_node.addr;
				spkg.loadData(data);
				Port_user.sendPkg(spkg);
				local_node.row.Cells[2].Value = $"本地模块{work_word}命令已经发出。";
			}
			await Task.Delay(1000);

			if (remote_node.Count > 0) {
				this.StatusLAB.Text = $"确认远程模块{work_word}命令正确";
				foreach (var node in remote_node) {
					Pkg spkg = new();
					spkg.To_port = 110;
					spkg.Remote_addr = node.addr;
					spkg.loadData(data);
					Port_user.sendPkg(spkg);
					node.row.Cells[2].Value = "命令再次发出，以确认设置成功。";
				}
				await Task.Delay(1000);
			}
			ERecvPkg -= recvPkg;
			if (remote_node.Count > 0) {
				foreach (var node in remote_node) {
					if (node.row.Cells[2].Value as string == "命令再次发出，以确认设置成功。") {
						node.row.Cells[2].Value = $"{work_word}失败。";
					}
				}
			}
			this.StatusLAB.Text = $"{work_word}完成";
		}

		private void passwdBTN_Click(object sender, EventArgs e) {
			PasswdGroup.Visible = true;
			passwdBTN.Image = passwd_act;
			dNodeSelected = r => {
				r.Cells[2].Value = "即将设定密码。";
			};
			dGoAction = passwdSettingAction;
			doActionBTN.Enabled = true;
			is_selected_all = false;
			SelectAllBTN_Click(sender, e);
		}

		private async void doActionBTN_Click(object sender, EventArgs e) {
			this.commonTS.Enabled = this.PasswdGroup.Enabled = NodesDGV.Enabled = false;
			await dGoAction();
			this.commonTS.Enabled = this.PasswdGroup.Enabled = NodesDGV.Enabled = true;

		}

		static Form help_form = null;
		private void HelpBTN_Click(object sender, EventArgs e) {
			if (help_form == null) {
				help_form = new HelpForm();
				help_form.FormClosing += (s, e) => {
					help_form = null;
				};
			}
			help_form.Show();
		}
	}
}
